#ifndef _ACE_MESH_RUNTIME_
#define _ACE_MESH_RUNTIME_

#ifdef __cplusplus
extern "C" {
#endif

#include <stdio.h>
#include <string.h>
// #include "aceMesh_task.h"
#include "am_machine.h"

#ifdef MEMORY_POOL
#include "MemPool.h"
#endif
#ifdef THREAD_TRACE
#include "thread_trace.h"
#endif
#ifdef NO_PARALLEL
#ifdef CONCURRENT_CONSTRUCT_GRAPH
#include "aceMesh_concurrent_task.h"
#else
#include "aceMesh_task.h"
#endif
#else

#endif

#include "task.h"
typedef int Error_Code;
#define ACEMESH_OK 0

#if defined(DYNAMIC_SCHEDULER)
#define ACEMESH_NEW new (tbb::task::allocate_root())
#elif defined(ACEMESH_SCHEDULER)
#define ACEMESH_NEW new
#endif

#define MAX_NEIGHBORS 27
// #define MAX_NEIGHBORS 100
#define LDM_NEIGHBORS 16
#define mymin(a, b) ((a < b) ? (a) : (b))

#ifdef __ACEMESH_THREAD_GROUP
// interfaces for hierarchical execution
Error_Code aceMesh_runtime_init(int total_threads, int group_size, int core_ids[]);
Error_Code aceMesh_runtime_init(int total_threads, int group_size);
Error_Code aceMesh_runtime_init(int total_threads);
Error_Code aceMesh_runtime_init(int total_threads, int core_ids[]);
Error_Code aceMesh_runtime_init();
void set_splitter_dim_chunks(size_t dim, ...);
#include "range.h"

typedef AceMesh_runtime::range_d range_d;
#endif
Error_Code aceMesh_runtime_init_TNum(int thread_num);
Error_Code aceMesh_runtime_init(int thread_num, int core_ids[]);
Error_Code aceMesh_runtime_init_zeroPara();
Error_Code aceMesh_runtime_shutdown();
Error_Code aceMesh_runtime_shutdown_with_proc_id(int proc_id);

// NEIGHBOR relationship
#define NEIGHBOR_NONE 0x00000000
#define NEIGHBOR_UP 0x00000001
#define NEIGHBOR_DOWN 0x00000002
#define NEIGHBOR_LEFT 0x00000004
#define NEIGHBOR_RIGHT 0x00000008
#define NEIGHBOR_FRONT 0x00000010
#define NEIGHBOR_BACK 0x00000020
#define NEIGHBOR_ALL 0x0000ffff

// the read/write flag for addr
#define INOUT_NONE 0
#define IN 1
#define OUT 2
#define INOUT 3

// the type flag for the area that stands for by addr
#define NORMAL 1
#define SHADE 2
#define UNSHADE 3
#define SHADE_AND_UNSHADE 4

// for task type
//  typedef int task_type;
#define NOT_SET 0
#define STENCIL_TASK 1
#define NOAFFINITY_TASK 2
#define BLOCKING_TASK 3  // for mpi_wait, or mpi_send,mpi_recv etc blocking ops.
#define COMPOSITE_TASK 4
#define COMPOSITE_END_TASK 5
// end

#define RTS_undefined_event "21"
#define RTS_event_end "00"

// n void* addr, short int area, int neighbor, int inout_type, int neighbor_inout_type
// eg: A[0], NORMAL, NEIGHBOR_ALL, INOUT, IN
// eg  B[0], SHADE_AND_UNSHADE, NEIGHBOR_NONE, INOUT, INOUT_NONE, (SHADE)
//                            NEIGHBOR_ALL,  INOUT_NONE, IN   , (UNSHADE)
// eg  C[0], SHADE, NEIGHBOR_NONE, INOUT, INOUT_NONE, (SHADE)
// eg  D[0], UNSHADE, NEIGHBOR_NONE, INOUT, INOUT_NONE, (UNSHADE)
Error_Code register_task(struct aceMesh_task *t, int n, ...);
Error_Code register_task_datainfo(struct aceMesh_task *t, int n, double *ad, int area, int nei,
                                  int ty, int ne);  //(aceMesh_task* t, int n, ...);
Error_Code register_task_end(struct aceMesh_task *t);

#ifdef __ACEMESH_THREAD_GROUP
// fine-grain task interface.
template <typename TaskBody, typename RangeType>
Error_Code register_task(TaskBody *task_body, const RangeType &range, int n, ...);
#endif

#ifdef REUSE_GRAPH
Error_Code dag_start(int dagNo, int *int_vec, int n1, double *float_vec, int n2);
#endif
void spawn_and_goout(void *taskptr);

#ifdef CONCURRENT_CONSTRUCT_GRAPH
void wait_for_all_task();
void acemesh_wait_for_all_task();
#endif

#ifdef THREAD_TRACE
void acemesh_trace_print(const char event_name[]);
void slave_acemesh_trace_print(const char event_name[]);
#endif

// Error_Code begin_split_task(const std::string& loop_info);
Error_Code begin_split_task_charPara(char *loop_info, void *taskptr);
Error_Code begin_split_task_zeroPara(void *taskptr);
Error_Code end_split_task_zeroPara(void *taskptr);

void spawn_and_wait_zeroPara(void *taskptr);
void spawn_and_wait(int print_graph, void *taskptr);

#if defined(AUTO_PARTITION) || defined(SUPPORT_PARTITION)
void spawn_and_wait_sep();
#endif

extern int num_registers;

int aceMesh_get_thread_id();
void aceMesh_MPI_rank(int rank);
void acemesh_noop_touch_integer(int *data);
void acemesh_noop_touch_real(double *data);

// debug interface
void print_to_thread_file(const char *format, ...);

// template implementation.
#ifdef __ACEMESH_THREAD_GROUP
#include <cstdarg>

#include "aceMesh_hierarchy_task.h"

void do_register(aceMesh_task *p_task, int n, va_list *p_args);

template <typename TaskBody, typename RangeType>
Error_Code register_task(TaskBody *task_body, const RangeType &range, int n, ...)
{
    aceMesh_task *hierarchy_task =
        ACEMESH_NEW AceMesh_runtime::aceMesh_hierarchy_task<TaskBody, RangeType>(task_body, range);

    va_list args;
    va_start(args, n);
    do_register(hierarchy_task, n, &args);
    // sort(addrs.begin(), addrs.end(), )
    va_end(args);

    return ACEMESH_OK;
}
#endif

/* *********************************
 * Sveral interfaces for clang     *
 * Author: Han Dong Ni             *
 * Date : 2013-12-31               *
 * *********************************/

typedef void (*TASK_FUNCPTR)(volatile void *args);
#ifndef SWF
typedef void (*NEIGHBOR_FUNCPTR)(void **neighbor_addrs, int *num_neigh, void *args);
#else
typedef void (*NEIGHBOR_FUNCPTR)(void *args);
#endif

void acemesh_runtime_init(int total_threads);
void acemesh_runtime_shutdown();
// Acemesh task generator
void acemesh_task_generator(void *taskfptr, void *args, unsigned int args_size);
void acemesh_task_generator_with_neighbors(void *taskfptr, void *args, unsigned int args_size,
                                           void *cxx_this_pointer, void *get_neighbors_funcptr,
                                           void *neighbor_args);

void acemesh_composite_task_generator(void *taskfptr, void *args, unsigned int args_size);
void acemesh_composite_task_generator_with_neighbors(void *taskfptr, void *args,
                                                     unsigned int args_size, void *cxx_this_pointer,
                                                     void *get_neighbors_funcptr,
                                                     void *neighbor_args);

// Handler for #pragma acemesh data clause
void acemesh_push_wrlist(int argc, void *addr, int access_flag, ...);
void acemesh_push_rlist(int argc, void *addr, int access_flag, ...);
void acemesh_push_wlist(int argc, void *addr, int access_flag, ...);
// for NEST
void slave_acemesh_push_wrlist(int argc, void *addr, int access_flag, ...);
void slave_acemesh_push_rlist(int argc, void *addr, int access_flag, ...);
void slave_acemesh_push_wlist(int argc, void *addr, int access_flag, ...);
void slave_acemesh_task_generator(void *taskfptr, void *args, unsigned int args_size);
void slave_acemesh_task_generator_with_neighbors(void *taskfptr, void *args, unsigned int args_size,
                                                 void *cxx_this_pointer,
                                                 void *get_neighbors_funcptr, void *neighbor_args);
// void slave_spawn_and_goout(void *taskptr);
// void slave_spawn_and_wait(int print_graph, void *taskptr);
// Error_Code slave_register_task(struct aceMesh_task *t);
#ifdef REUSE_GRAPH
int acemesh_dag_start(int dagNo);
#endif

void acemesh_begin_split_task(char *loop_info);
void acemesh_end_split_task();
void acemesh_spawn_and_wait(int print_graph);

void *acemesh_malloc_obj(unsigned int args_size);

void acemesh_task_map_master();
void acemesh_set_suspend(int mode);
void acemesh_task_set_type(int type);

#ifdef SUPPORT_PARTITION
void acemesh_setsepid(int id);
#endif

void *aceMesh_get_data();
void aceMesh_set_data(void *data);
int get_part_id();
void set_part_id(int part_id);

#ifdef __cplusplus
}
#endif
#endif
